
#include "board.h"
#include "uart.h"
#include "gpio.h"
#include "utils/byte_queue.h"
#include "stm32g4xx_ll_dma.h"
#include "stm32g4xx_ll_usart.h"
#include "stm32g4xx_ll_rcc.h"
#define TX_CACHE_SIZE CONFIG_UART_TX_SIZE
#define RX_CACHE_SIZE CONFIG_UART_RX_SIZE

u8 tx_cache[TX_CACHE_SIZE];
u8 rx_cache[RX_CACHE_SIZE];
byte_queue_t uart_tx_queue;
byte_queue_t uart_rx_queue;
static u16 uart_tx_length = 0;
static u16 uart_rx_index = 0;
static bool _b_inited = false;
static void MX_USART3_UART_Init(void);
static void MX_DMA_Init(void);

void uart_init() {
	if (_b_inited) {
		return;
	}
	_b_inited = true;
    byte_queue_init(&uart_tx_queue, tx_cache, TX_CACHE_SIZE);
	byte_queue_init(&uart_rx_queue, rx_cache, RX_CACHE_SIZE);
	MX_DMA_Init();
    MX_USART3_UART_Init();
}

void uart_deinit(void) {
	if (!_b_inited) {
		return;
	}
	_b_inited = false;
	
}

void uart_tx_poll(void) {
	if (LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_4)) {
		if (SET != LL_DMA_IsActiveFlag_TC4(DMA1)) {
			return;
		}
		byte_queue_skip(&uart_tx_queue, uart_tx_length);
		LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_4);
	}

	uart_tx_length = byte_queue_peek(&uart_tx_queue);
	if (uart_tx_length > 0) {
		LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_4, uart_tx_length);
		LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_4, (u32) byte_queue_head(&uart_tx_queue));
		LL_DMA_ClearFlag_TC4(DMA1);
		if (rs485_rx_is_enabled()) {
			rs485_rx_enable(false);
		}
		LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_4);
	}
	if (!LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_4) && LL_USART_IsActiveFlag_TC(USART3)) {
		rs485_rx_enable(true);
		LL_USART_ClearFlag_TC(USART3);
	}
}

void uart_rx_poll(void) {

	u16 index = uart_rx_index;

	uart_rx_index = sizeof(rx_cache) - LL_DMA_GetDataLength(DMA1, LL_DMA_CHANNEL_3);
	if (uart_rx_index < index) {
		serial_on_data_received(rx_cache + index, sizeof(rx_cache) - index);
		serial_on_data_received(rx_cache, uart_rx_index);
	} else {
		serial_on_data_received(rx_cache + index, uart_rx_index - index);
	}
}


static void MX_USART3_UART_Init(void)
{

  /* USER CODE BEGIN USART3_Init 0 */

  /* USER CODE END USART3_Init 0 */

  LL_USART_InitTypeDef USART_InitStruct = {0};

  //LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

  LL_RCC_SetUSARTClockSource(LL_RCC_USART3_CLKSOURCE_PCLK1);

  /* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART3);

  LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB);
  /**USART3 GPIO Configuration
  PB8-BOOT0   ------> USART3_RX
  PB9   ------> USART3_TX
  */
#if 0  
  GPIO_InitStruct.Pin = LL_GPIO_PIN_8;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
  LL_GPIO_Init(GPIOB, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = LL_GPIO_PIN_9;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
  LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
#endif
    gpio2_af_init(PIO(PORTB, PIN8), GPIO_MODE_AF_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_LOW, GPIO_AF7_USART3);
    gpio2_af_init(PIO(PORTB, PIN9), GPIO_MODE_AF_PP, GPIO_NOPULL, GPIO_SPEED_FREQ_LOW, GPIO_AF7_USART3);
  /* USART3 DMA Init */

  /* USART3_RX Init */
  LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_3, LL_DMAMUX_REQ_USART3_RX);

  LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_3, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);

  LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PRIORITY_LOW);

  LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MODE_CIRCULAR);

  LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PERIPH_NOINCREMENT);

  LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MEMORY_INCREMENT);

  LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_PDATAALIGN_BYTE);

  LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_3, LL_DMA_MDATAALIGN_BYTE);

  /* USART3_TX Init */
  LL_DMA_SetPeriphRequest(DMA1, LL_DMA_CHANNEL_4, LL_DMAMUX_REQ_USART3_TX);

  LL_DMA_SetDataTransferDirection(DMA1, LL_DMA_CHANNEL_4, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);

  LL_DMA_SetChannelPriorityLevel(DMA1, LL_DMA_CHANNEL_4, LL_DMA_PRIORITY_LOW);

  LL_DMA_SetMode(DMA1, LL_DMA_CHANNEL_4, LL_DMA_MODE_NORMAL);

  LL_DMA_SetPeriphIncMode(DMA1, LL_DMA_CHANNEL_4, LL_DMA_PERIPH_NOINCREMENT);

  LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_4, LL_DMA_MEMORY_INCREMENT);

  LL_DMA_SetPeriphSize(DMA1, LL_DMA_CHANNEL_4, LL_DMA_PDATAALIGN_BYTE);

  LL_DMA_SetMemorySize(DMA1, LL_DMA_CHANNEL_4, LL_DMA_MDATAALIGN_BYTE);

  /* USER CODE BEGIN USART3_Init 1 */

  /* USER CODE END USART3_Init 1 */
  USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
  USART_InitStruct.BaudRate = 115200;
  USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
  USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
  USART_InitStruct.Parity = LL_USART_PARITY_NONE;
  USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
  USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
  USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
  LL_USART_Init(USART3, &USART_InitStruct);
  LL_USART_SetTXFIFOThreshold(USART3, LL_USART_FIFOTHRESHOLD_1_8);
  LL_USART_SetRXFIFOThreshold(USART3, LL_USART_FIFOTHRESHOLD_1_8);
  LL_USART_EnableFIFO(USART3);
  LL_USART_ConfigAsyncMode(USART3);

  /* USER CODE BEGIN WKUPType USART3 */

  /* USER CODE END WKUPType USART3 */

  LL_USART_Enable(USART3);

  /* Polling USART3 initialisation */
  while((!(LL_USART_IsActiveFlag_TEACK(USART3))) || (!(LL_USART_IsActiveFlag_REACK(USART3))))
  {
  }
  /* USER CODE BEGIN USART3_Init 2 */

  /* USER CODE END USART3_Init 2 */

}

/**
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void)
{

  /* Init with LL driver */
  /* DMA controller clock enable */
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMAMUX1);
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
}